# coding=UTF-8
import base64
import inspect
import sys
import time
import winreg

import pygame
import pymysql
import requests
import win32print
import xlwings
import xlwt
from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtGui import QIcon
from PyQt5.QtSql import QSqlTableModel, QSqlQuery, QSqlDatabase
from PyQt5.QtWidgets import QAbstractItemView, QHeaderView, QMessageBox, qApp, QTableView
from PyQt5.QtWidgets import QAction, QMenu, QSystemTrayIcon
from PyQt5.QtWidgets import QMainWindow
from dateutil.parser import parse
from loguru import logger


class 常量:
	# 集港时间模块:
	集港时间_无关灰 = QColor(167, 167, 167)
	集港时间_进行绿 = QColor(0, 255, 0)
	集港时间_未来蓝 = QColor(0, 255, 255)
	集港时间_场站列号 = 4
	集港时间_开始列号 = 6
	集港时间_结束列号 = 7
	集港时间_起止相差天数 = 5
	
	# 综合查询模块：
	综合查询_信息表格初始高度 = 60
	综合查询_船舶计划表格初始高度 = 60
	
	综合查询_船舶计划表格中文船名列号 = 1
	
	综合查询_船舶计划表格英文船名列号 = 5
	综合查询_船舶计划表格船名航次列号 = 2
	综合查询_信息过期灰 = QColor(167, 167, 167)
	综合查询_信息最近绿 = QColor(0, 255, 0)
	综合查询_船舶计划绿 = QColor(0, 255, 0)
	
	综合查询_无关灰 = QColor(167, 167, 167)
	综合查询_进行绿 = QColor(0, 255, 0)
	综合查询_未来蓝 = QColor(0, 255, 255)
	
	# 主程序模块：
	主程序_日志框高度 = 120
	
	# 多样查询模块：
	多样查询_栏数 = 2
	多样查询_第一栏宽 = 300
	
	# 最近船期模块：
	最近船期_船名代码列号 = 3
	最近船期_中文船名列号 = 6
	最近船期_出口航次列号 = 1
	最近船期_码头列号 = 4
	最近船期_船期列号 = 5
	
	# 船期查询模块：
	船期查询_后缀范围 = ['xls', 'XLS', 'Xls', 'xlsx', 'XLSX', 'Xlsx']
	
	# 计件统计模块：
	计件统计_后缀范围 = ['xls', 'XLS', 'Xls', 'xlsx', 'XLSX', 'Xlsx']
	
	# 笔趣阁小说模块
	小说表格表头 = ['小说名', '作者', '最新章节', '字数', 'ID', '打开', '下载', '进度', '最后更新时间', '状态']
	章节表格表头 = ['章节序号', '章节题目', '字数', 'ID', '打开', '下载', '进度', '备注']
	
	# 异常翻译模块：
	异常翻译 = {
		'BaseException': '所有异常的基类',
		'SystemExit': '解释器请求退出',
		'KeyboardInterrupt': '用户中断执行(通常是输入^C)',
		'GeneratorExit': '生成器(generator)发生异常来通知退出',
		'Exception': '常规错误的基类',
		'StopIteration': '迭代器没有更多值',
		'StopAsyncIteration': '必须通过异步迭代器对象的__anext__()方法引发以停止迭代',
		'ArithmeticError': '所有数值计算错误的基类',
		'FloatingPointError': '浮点计算错误',
		'OverflowError': '数值运算超出最大限制',
		'ZeroDivisionError': '除(或取模)零 (所有数据类型)',
		'AssertionError': '断言语句失败',
		'AttributeError': '对象没有这个属性',
		'BufferError': '与缓冲区相关的操作时引发',
		'EOFError': '没有内建输入,到达EOF 标记',
		'ImportError': '导入失败',
		'ModuleNotFoundError': '找不到模块',
		'LookupError': '无效数据查询的基类',
		'IndexError': '序列中没有此索引',
		'KeyError': '映射中没有这个键',
		'MemoryError': '内存溢出错误',
		'NameError': '未声明、初始化对象',
		'UnboundLocalError': '访问未初始化的本地变量',
		'OSError': '操作系统错误',
		'BlockingIOError': '操作将阻塞对象设置为非阻塞操作',
		'ChildProcessError': '子进程上的操作失败',
		'ConnectionError': '与连接相关的异常的基类',
		'BrokenPipeError': '在已关闭写入的套接字上写入',
		'ConnectionAbortedError': '连接尝试被对等方中止',
		'ConnectionRefusedError': '连接尝试被对等方拒绝',
		'ConnectionResetError': '连接由对等方重置',
		'FileExistsError': '创建已存在的文件或目录',
		'FileNotFoundError': '请求不存在的文件或目录',
		'InterruptedError': '系统调用被输入信号中断',
		'IsADirectoryError': '在目录上请求文件操作',
		'NotADirectoryError': '在不是目录的事物上请求目录操作',
		'PermissionError': '在没有访问权限的情况下运行操作',
		'ProcessLookupError': '进程不存在',
		'TimeoutError': '系统函数在系统级别超时',
		'ReferenceError': '弱引用试图访问已经垃圾回收了的对象',
		'RuntimeError': '一般的运行时错误',
		'NotImplementedError': '尚未实现的方法',
		'RecursionError': '解释器检测到超出最大递归深度',
		'SyntaxError': 'Python 语法错误',
		'IndentationError': '缩进错误',
		'TabError': 'Tab 和空格混用',
		'SystemError': '一般的解释器系统错误',
		'TypeError': '对类型无效的操作',
		'ValueError': '传入无效的参数',
		'UnicodeError': 'Unicode 相关的错误',
		'UnicodeDecodeError': 'Unicode 解码时的错误',
		'UnicodeEncodeError': 'Unicode 编码时错误',
		'UnicodeTranslateError': 'Unicode 转换时错误',
		'Warning': '警告的基类',
		'DeprecationWarning': '关于被弃用的特征的警告',
		'PendingDeprecationWarning': '关于构造将来语义会有改变的警告',
		'RuntimeWarning': '可疑的运行行为的警告',
		'SyntaxWarning': '可疑的语法的警告',
		'UserWarning': '用户代码生成的警告',
		'FutureWarning': '有关已弃用功能的警告的基类',
		'ImportWarning': '模块导入时可能出错的警告的基类',
		'UnicodeWarning': '与Unicode相关的警告的基类',
		'BytesWarning': 'bytes和bytearray相关的警告的基类',
		'ResourceWarning': '与资源使用相关的警告的基类'
	}
	
	# 日志配置
	文本格式 = "<lvl>{time:YYYY-MM-DD HH:mm:ss} |级别:{level:5}|模块:{module}|方法:{function}|行号:{line:2}|{message}</>"
	工作日志 = '日志文件/工作日志.log'
	异常日志 = '日志文件/异常日志.log'
	
	# 托盘配置
	托盘_图标 = '图标\\test.ico'
	
	# 补工作证
	补工作证_初始数据 = f'''
  山东捷丰国际储运有限公司
        入港单/工作证
------------------------------
船名/航次:******/******
箱号:***********
尺寸/箱型:****
备注:补打工作证
------------------------------
起始地点:******
卸箱地点:******
单/背:**     空重:**
箱号:***********
车队:**
车号：**
工作证号:***********
集港时间:***********
打印时间:***********
打印人:******
------------------------------
联系电话:前湾80659523
  港服86058731
  四期15054295403

---------------END------------
'''
	补工作证_表头 = ['箱号', '箱型', '尺寸', '空重', '是否超重箱', '工作证号', '船名(中)', '船名(英)', '航次', '车号',
	                 '车队', '入港时间', '打印时间', '打印人', '入港方式', '起运地点', '港区']
	
	# 依赖环境配置
	依赖环境 = ['工具类.py', '雨晨科技系统.py', 'test.ico', 'test2.ico', 'test3.ico', '工作日志.log', '异常日志.log',
	            '主窗口.py', '主题设置.py',
	            '出口下货纸.py', '动态大类维护.py', '动态类型维护.py', '场站维护.py', '外点返场.py', '多样查询.py',
	            '客户维护.py', '尺寸维护.py', '属性设置.py',
	            '工作日志.py', '常量.py', '拖车类型维护.py', '最近船期.py', '测试.py', '用户管理.py', '登录窗口.py',
	            '笔趣阁小说.py', '笔趣阁章节.py',
	            '箱型维护.py', '箱属维护.py', '综合查询.py', '航次维护.py', '船名维护.py', '船期查询.py', '补工作证.py',
	            '表格改造.py', '计件统计.py',
	            '费目维护.py', '车队维护.py', '酷狗音乐.py', '集港出场.py', '集港时间.py', '验证码.png',
	            '工作日志配置.ini',
	            'UI界面按钮点击音效_爱给网_aigei_com.mp3', '启动.mp3', '断开.mp3', '点击.mp3', '登录.mp3',
	            '警报-危险错误-游戏提示_爱给网_aigei_com.mp3',
	            '警报声-哔哔哔-错误提示音-综艺节目_爱给网_aigei_com.mp3']
	
	# 权限配置
	游客权限 = '{"用户维护": True, "运港通登录": True, "运港通快捷登录": True, "综合查询": False, "多样查询": False, "船期查询": False, "最近船期": False, "集港时间": False, "外点返场": False, "集港出场": False, "出口下货纸": False, "补工作证": True, "表格改造": True, "计件统计": True, "场站维护": True, "尺寸维护": True, "箱型维护": True, "箱属维护": True, "客户维护": True, "航次维护": True, "费目维护": True, "车队维护": True, "船名代码维护": True, "动态类型维护": True, "动态大类维护": True, "拖车类型维护": True, "超级管理员维护": True, "笔趣阁": False, "酷狗音乐": False, "属性设置": True, "权限设置": True, "显示日志框": True}'
	
	# 超级管理员超看的表名
	超级管理员超看的表名 = ['zhuti', 'zhanghaomima', 'yuangongxinxi', 'yonghu', 'xinxitongji', 'xiangshu',
	                        'chuanboziliao',
	                        'chuanboziliao', 'changzhan']


logger.remove()
logger.add(sys.stderr, format=常量.文本格式, filter=lambda record: record["level"].name in ["DEBUG", "INFO", "ERROR"])
logger.add(常量.工作日志, format=常量.文本格式, filter=lambda record: record["level"].name == "INFO")
logger.add(常量.异常日志, format=常量.文本格式, filter=lambda record: record["level"].name == "ERROR")

返回容器 = {'结果': False,
            '内容': [{}],
            '状态码': '',
            '状态描述': '',
            '数量': '',
            '原文': '', }


def 追溯(信息):
	caller_frames = inspect.stack()[1:]
	文本 = ''
	for caller_frame in caller_frames:
		caller_file = caller_frame[1]
		caller_line = caller_frame[2]
		caller_function = caller_frame[3]
		文本 += f'【{caller_file}--:--{caller_function}-:-{caller_line}】<--'
	print(f'追溯：{信息}--:{文本}')


def 播放音乐(音乐文件):
	try:
		pygame.mixer.init()  # 初始化混音器
		pygame.mixer.music.load(音乐文件)  # 加载音乐文件
		pygame.mixer.music.play()  # 播放音乐
	except Exception as e:
		logger.exception(f"播放音乐时发生错误: {e}")


def 时间到操作人(文本时间):
	时间0 = parse('2022-06-10 00:00:00')
	班安排早 = {'0': '刘传勇', '1': '刘传勇', '2': 'TQCZ010', '3': 'TQCZ010', '4': ' 迟栓龙', '5': ' 迟栓龙'}
	班安排中 = {'0': 'TQCZ010', '1': ' 迟栓龙', '2': ' 迟栓龙', '3': '刘传勇', '4': '刘传勇', '5': 'TQCZ010'}
	班安排晚 = {'0': '刘传勇', '1': 'TQCZ010', '2': 'TQCZ010', '3': ' 迟栓龙', '4': ' 迟栓龙', '5': '刘传勇'}
	
	if 文本时间 != "空值":
		return "空值"
	try:
		时间 = parse(str(文本时间))
		上班日期 = 时间.strftime("%Y-%m-%d")
		班次 = str((时间 - 时间0).days % 6)
		
		if parse(上班日期 + ' 00:00:00') <= 时间 < parse(上班日期 + ' 08:30:00'):
			操作人 = 班安排早[班次]
		elif parse(上班日期 + ' 08:30:00') <= 时间 < parse(上班日期 + ' 17:30:00'):
			操作人 = 班安排中[班次]
		elif parse(上班日期 + ' 17:30:00') <= 时间 < parse(上班日期 + ' 23:59:59'):
			操作人 = 班安排晚[班次]
		else:
			操作人 = '不知道'
		return 操作人
	except Exception as e:
		logger.exception(e)
		return '时间格式不对'


def 日期到操作人(文本日期):
	try:
		时间0 = parse('2024-08-01')
		if 文本日期 == "空值":
			return "空值"
		
		时间 = parse(文本日期)
		班次 = (时间 - 时间0).days % 6
		操作人 = {}
		
		if 班次 in [0, 1]:
			操作人['白班'] = '刘传勇'
			操作人['夜班'] = ' 迟栓龙'
		elif 班次 in [2, 3]:
			操作人['白班'] = 'TQCZ010'
			操作人['夜班'] = '刘传勇'
		elif 班次 in [4, 5]:
			操作人['白班'] = ' 迟栓龙'
			操作人['夜班'] = 'TQCZ010'
		
		return 操作人
	except Exception as e:
		logger.exception(f"【{__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")


def 补打工作证(箱信息):
	try:
		数据 = f'''
        	山东捷丰国际储运有限公司
        		入港单/工作证
        ------------------------------
        船名/航次:{箱信息.get('船名', '海丰恒德')}/{箱信息.get('航次', '2306S')}
        箱号:{箱信息.get('箱号', 'TGBU2574913')}
        尺寸/箱型:{箱信息.get('尺寸', '20')}{箱信息.get('箱型', 'GP')}
        备注:补打工作证
        ------------------------------
        起始地点:{箱信息.get('起始地点', '捷丰南港重箱')}
        卸箱地点:{箱信息.get('卸箱地点', 'QQCTU')}
        单/背:{箱信息.get('入港方式', '单背')}     空重:{箱信息.get('空重', 'F')}
        箱号:{箱信息.get('箱号', 'TGBU2574913')}
        车队:{箱信息.get('车队', '元创初心')}
        车号：{箱信息.get('车号', '鲁UR9520')}
        工作证号:{箱信息.get('工作证号', 'J20230619000738')}
        集港时间:{箱信息.get('入港时间', '2023-06-17 22:00：00')}
        打印时间:{箱信息.get('打印时间', '2023-06-20 07:00:00')}
        打印人:{箱信息.get('打印人', '盈思佳德1出口')}
        ------------------------------
        联系电话:前湾80659523
        	 港服86058731
        	 四期15054295403
        ---------------END------------
        '''
		打印数据(数据)
	except Exception as e:
		logger.exception(f"【{__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")


def 打印数据(数据):
	数据 = bytes(数据, "GB2312")
	打印机名称 = win32print.GetDefaultPrinter()
	
	try:
		打印机对象 = win32print.OpenPrinter(打印机名称)
	except Exception as e:
		logger.exception(e)
		return
	try:
		win32print.StartDocPrinter(打印机对象, 1, ("test of raw data", None, "TEXT"))
		win32print.StartPagePrinter(打印机对象)
		win32print.WritePrinter(打印机对象, 数据)
		win32print.EndPagePrinter(打印机对象)
	finally:
		win32print.EndDocPrinter(打印机对象)
		win32print.ClosePrinter(打印机对象)


class 网络连接:
	
	def __init__(self):
		try:
			self.session = requests.Session()
			self.AccessToken = self.accesstoken()
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def accesstoken(self, id号='1QG0Mmopb0m02tgzzRtggEh5', secret='1IDXsFfogqaGcwYkHxONfzsKzH0jIf2U',
	                grant_type='client_credentials'):
		data = {
			'grant_type': grant_type,
			'client_id': id号,
			'client_secret': secret
		}
		
		url = 'https://aip.baidubce.com/oauth/2.0/token'
		try:
			返回值 = self.session.post(url=url, data=data)
			返回值.raise_for_status()  # 添加状态码检查，抛出异常
			return 返回值.json().get('access_token', "默认访问令牌")
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP错误: {http_err}, 响应内容: {返回值.text if '返回值' in locals() else ''}")
		except Exception as e:
			logger.exception(f"获取 AccessToken 过程中发生错误: {e}")
		
		return "24.f18e5c8f6ec8fe4d4d424e8f8552abe3.2592000"  # 使用默认令牌作为后备
	
	def 获取图片(self, 图片地址='https://www.qingdao-port.net/ygt/api/safe/captcha.do?1556425983801'):
		requests.packages.urllib3.disable_warnings()  # 禁用警告
		
		try:
			返回值 = self.session.get(图片地址, verify=False)
			返回值.raise_for_status()  # 检查请求是否成功
			
			if 返回值.text in ['', '{}']:
				return {'结果': False, '状态码': '404', '状态描述': '获取图片返回空白值'}
			
			with open("缓存/验证码.png", "wb") as f:  # 使用正斜杠，避免路径问题
				f.write(返回值.content)
			
			return {'结果': True, '数据': 返回值.content, '原文': 返回值.text}
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP错误: {http_err}")
			return {'结果': False, '状态码': 'HTTP错误', '状态描述': str(http_err), '原文': 'HTTP请求失败'}
		except Exception as e:
			logger.exception(f"获取图片时发生错误: {e}")
			return {'结果': False, '状态码': '异常', '状态描述': str(e), '原文': '连接失败，可能没有网络'}
	
	def 识别图片(self, 图片='缓存/验证码.png', 方式=1):
		try:
			with open(图片, 'rb') as f:
				img = base64.b64encode(f.read())
			
			识别方式 = {
				'1': {'方式': '通用文字识别（高精度版）',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token='},
				'2': {'方式': '通用文字识别（高精度含位置版）',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate?access_token='},
				'3': {'方式': '通用文字识别（标准版）',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token='},
				'4': {'方式': '通用文字识别（标准含位置版）',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/general?access_token='},
				'5': {'方式': '网络图片文字识别',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/webimage?access_token='},
				'6': {'方式': '网络图片文字识别（含位置版）',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/webimage_loc?access_token='},
				'7': {'方式': '手写文字识别',
				      'url': 'https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting?access_token='}
			}
			
			url = 识别方式[str(方式)]['url'] + self.AccessToken
			headers = {'content-type': 'application/x-www-form-urlencoded'}
			
			返回值 = self.session.post(url, data={"image": img}, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回数据 = 返回值.json()
			数据 = 返回数据.get('words_result', [{}])[0].get('words', '???').replace(" ", "")
			return {
				'方式': 识别方式[str(方式)]['方式'],
				'状态码': 返回数据.get("error_code", '无状态码数据返回'),
				'状态描述': 返回数据.get("error_msg", '无状态描述数据返回'),
				'结果': not 返回数据.get("error_code"),
				'数据': 数据,
				'原文': 返回值.text
			} if 返回值.text else {'结果': False, '状态码': '404', '状态描述': '识别图片返回空白数据',
			                       '原文': 返回值.text}
		
		except Exception as e:
			logger.exception(f"识别图片时发生错误: {e}")
			return {'结果': False, '状态码': '异常', '状态描述': str(e)}
	
	def 登录(self, 账号, 密码, 验证码, 网址='https://www.qingdao-port.net/ygt/api/sso/login.do'):
		data = {'userName': 账号, 'passWord': 密码, 'code': 验证码}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}
		
		返回 = {'账号': 账号, '密码': 密码, '验证码': 验证码, '结果': False}
		
		try:
			返回值 = self.session.post(网址, data=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			
			返回json = 返回值.json()
			返回['结果'] = 返回json.get('success', False)
			返回['状态码'] = '200' if 返回['结果'] else '错误'
			返回['状态描述'] = 返回json.get('msg', '未知错误')
			返回['原文'] = 返回值.text
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(http_err)
		except Exception as e:
			logger.exception(f"登录错误: {e}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(e)
		
		return 返回
	
	def 快速登录(self, 账号, 密码):
		for i in range(1, 8):
			for j in range(1, 11):
				图片 = self.获取图片()
				if not 图片.get('结果', False):
					logger.info(f'{i}-第{j}次登陆失败！{图片["状态描述"]}')
					continue
				
				识图 = self.识别图片(方式=i)
				if not 识图.get('结果', False):
					logger.info(f'{i}-第{j}次登陆失败！{识图["状态描述"]}')
					continue
				
				登录信息 = self.登录(账号, 密码, 识图.get('数据', 'xxxx'))
				if 登录信息.get('结果', False):
					登录信息['登录次数'] = j
					logger.info(f'{i}-第{j}次登陆成功!')
					return 登录信息
				
				logger.info(f'{i}-第{j}次登陆失败！{登录信息["状态描述"]}')
		
		return {'结果': False, '状态描述': '所有尝试均失败'}
	
	def 登录返回信息(self):
		url = 'https://api.qingdao-port.net/user/v2/member/getUserData.do'
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		返回 = {'结果': False, '状态码': '404', '状态描述': '登录返回信息无响应', '原文': ''}
		
		try:
			返回值 = self.session.get(url, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			
			返回json = 返回值.json()
			返回['状态码'] = str(返回值.status_code)
			
			if 返回值.status_code == 200:
				返回['结果'] = True
				返回['用户名'] = 返回json.get('user', {}).get('realName', '游客')
				返回['用户公司'] = 返回json.get('user', {}).get('companyName', '个人')
				返回['状态描述'] = 返回json.get('msg', '成功')
			else:
				返回['状态描述'] = 返回json.get('msg', '未知错误')
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP错误: {http_err}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(http_err)
		except Exception as e:
			logger.exception(f"获取登录返回信息时发生异常: {e}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(e)
		
		return 返回
	
	def 最近船舶计划(self, 内容, 码头='QQCT'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/cbjhcmcx.do'
		data = {'term': 内容, 'mtmc': 码头}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		返回 = {}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers, verify=False)
			返回值.raise_for_status()  # 检查请求是否成功
			
			if 返回值.text in ('', '{}'):
				返回['结果'] = False
				返回['状态码'] = '404'
				返回['状态描述'] = '最近船舶计划返回空白值'
				返回['原文'] = 'text'
				return 返回
			
			所有船 = 返回值.json()
			返回['数量'] = len(所有船)
			返回['数据'] = []
			返回['原文'] = 返回值.text
			
			if 返回['数量'] == 0:
				返回['结果'] = True
				返回['状态码'] = '0'
				返回['状态描述'] = '最近船舶计划数量=0'
			else:
				for i in 所有船:
					timestamp = int(i.get('ETA', '946656001000')) / 1000  # 转换时间戳
					formatted_date = time.strftime('%Y-%m-%d %H:%M', time.localtime(timestamp))
					船 = {
						'进口航次': i.get('JKHC', ''),
						'英文船名': i.get('YWCM', ''),
						'船名代码': i.get('CMDM', ''),
						'码头': i.get('KBDW', ''),
						'预计到港时间': formatted_date,
						'中文船名': i.get('ZWCM', ''),
						'出口航次': i.get('CKHC', '')
					}
					返回['数据'].append(船)
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['结果'] = False
			返回['状态码'] = '异常'
			返回['状态描述'] = str(http_err)
			返回['原文'] = '连不上'
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			返回['结果'] = False
			返回['状态码'] = '异常'
			返回['状态描述'] = str(e)
			返回['原文'] = '连不上'
		
		return 返回
	
	def 集装箱船舶计划(self, 船名代码, 中文船名, 出口航次, 进口航次='8S', 码头位置='QQCT', pageNum='1', pageSize='10'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/cbjhcx.do'
		data = {
			'pageNum': pageNum,
			'pageSize': pageSize,
			'ZWCM': 中文船名,
			'CKHC': 出口航次,
			'JKHC': 进口航次,
			'CMDM': 船名代码,
			'MTMC': 码头位置
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		返回 = {
			'结果': False,
			'状态码': '404',
			'状态描述': '请求未完成',
			'原文': '',
			'数据': []
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回json = 返回值.json()
			if not 返回json.get('msg', False) == '请求成功':
				返回['状态描述'] = 返回json.get('msg', '登录超时,请重新登录')
				return 返回
			数据 = 返回json.get('res', [{}])[0].get('data', [])
			
			for i in 数据:
				g = {'进口航次': i.get('JKHC', ''), '出口航次': i.get('CKHC', ''),
				     '抵港状态': i.get('DGBZ', ''), '航线': i.get('HXZM', ''),
				     'IMO号': i.get('CIMO', ''), '承运人': i.get('CYR', ''),
				     '预计离港时间': i.get('ETD', ''), '预计到港时间': i.get('ETA', ''),
				     '收箱结束时间': i.get('SXJSSJ', ''), '船名代码': i.get('CMDM', ''),
				     '英文船名': i.get('YWCM', ''), '内外贸': i.get('NWM', ''),
				     '进口代理': i.get('JKDL', ''), '泊位': i.get('BWDM1', ''),
				     '码头': i.get('DLWZ', ''), '航线代码': i.get('HXDM', ''),
				     '中文船名': i.get('ZWCM', '')}
				返回['数据'].append(g)
			返回['数量'] = len(返回['数据'])
			返回['状态码'] = '200'
			返回['状态描述'] = '请求成功'
			返回['结果'] = True
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP错误: {http_err}")
			返回['状态描述'] = str(http_err)
		except ValueError as ve:
			logger.exception(f"JSON解析错误: {ve}")
			返回['状态描述'] = '返回数据解析失败'
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态描述'] = str(e)
		
		返回['原文'] = 返回值.text
		return 返回
	
	def 收箱截货计划(self, 码头位置, 中文船名, 英文船名, 出口航次, 船名代码, pageNum='1', pageSize='10'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/queryqusxjh.do'
		data = {
			'pageNum': pageNum,
			'pageSize': pageSize,
			'ZWCM': 中文船名,
			'CKHC': 出口航次,
			'YWCM': 英文船名,
			'CMDM': 船名代码,
			'MTMC': 码头位置,
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		返回 = {
			'结果': False,
			'状态码': '404',
			'状态描述': '请求未完成',
			'原文': '',
			'数据': []}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回json = 返回值.json()
			if not 返回json.get('msg', False) == '请求成功':
				返回['状态描述'] = 返回json.get('msg', '未知错误')
				return 返回
			
			返回['数量'] = 返回json.get('res', [{}])[0].get('total', [])
			数据 = 返回json.get('res', [{}])[0].get('data', [])
			for i in 数据:
				g = {'码头': 码头位置,
				     '进口航次': i.get('JKHC', ''),
				     '收箱开始时间': i.get('SXKSSJ', ''),
				     '加急标志': i.get('JJBS', ''),
				     '箱站代码': i.get('XZDM', ''),
				     '箱属公司码': i.get('XSGSM', ''),
				     '空重': i.get('KZ', ''),
				     '船名代码': i.get('CMDM', ''),
				     '中文船名': i.get('ZWCM', ''),
				     '出口航次': i.get('CKHC', ''),
				     '收箱结束时间': i.get('SXJSSJ', ''),
				     '加急截止时间': i.get('JJJZSJ', ''),
				     '备注': i.get('BZBZ', '')}
				返回['数据'].append(g)
			返回['结果'] = True
			返回['状态码'] = '200'
			返回['状态描述'] = '请求成功'
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态描述'] = str(http_err)
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态描述'] = str(e)
		finally:
			返回['原文'] = 返回值.text if '返回值' in locals() else '连不上'
			return 返回
	
	def 装箱单(self, 箱号, pageNum='1', pageSize='10', tableId=''):
		url = 'https://api.qingdao-port.net/oceantally/logistics/wmdp/queryZxd.do'
		data = {'pageNum': pageNum, 'pageSize': pageSize, 'searchMap[XH]': 箱号, 'searchMap[tableId]': tableId}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			信息 = 返回值.json()  # 尝试转化为JSON
			if not 信息.get('msg', False) == '请求成功':
				return {'结果': False, '状态码': '异常', '状态描述': 信息.get('msg', '未知错误'), '原文': 返回值.text}
			数据 = 信息.get('res', [{}])[0].get('data', [])
			返回 = {
				'结果': True,
				'状态码': '200',
				'数量': len(数据),
				'数据': [{'称重方式': i.get('CZFS', ''), '毛重': i.get('MZ', ''), '箱型': i.get('XX', ''),
				          '码头': i.get('MTMC', ''), '航次': i.get('HCHC', ''),
				          '箱属公司码': i.get('XSGSM', ''), '船名代码': i.get('CMDM', ''),
				          '分单号': i.get('XSTDH', ''), '回执描述': i.get('HZMS', ''),
				          '内外贸': i.get('NWM', ''), '英文船名': i.get('YWCM', ''),
				          '箱站代码': i.get('XZDM', ''), '提单号': i.get('TDH', ''),
				          '提前申报标志': i.get('TQSBBZ', ''), '箱皮重': i.get('XPZ', ''),
				          '接受时间': i.get('INSERTSJ', ''), '回执状态': i.get('HZZT', ''),
				          '卸货港': i.get('XHGM', ''), '中文船名': i.get('ZWCM', ''),
				          '进口航次': i.get('JKHC', '')} for i in 数据],
				'原文': 返回值.text
			}
			return 返回
		except requests.RequestException as e:
			logger.exception(f"请求失败: {e}")
			return {'结果': False, '状态码': '异常', '状态描述': str(e), '原文': '连不上'}
		except ValueError as e:
			logger.exception(f"JSON解析失败: {e}")
			return {'结果': False, '状态码': '异常', '状态描述': '装箱单返回无效JSON', '原文': 返回值.text}
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			return {'结果': False, '状态码': '异常', '状态描述': str(e), '原文': '连接失败'}
	
	def 船舶资料(self, 内容, pageNum='1', pageSize='10', flag='0'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/cbzl.do'
		data = {'pageNum': pageNum, 'pageSize': pageSize, 'zwcm': 内容, 'flag': flag}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
			'Cookie': 'JSESSIONID_QDPORT=c9477558-f028-44cd-a940-045eb541efab; qdport-token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxODY2MTg4MzE5MSIsImlwIjoiMTEyLjYuMTE1LjY3IiwiaWQiOiIxNWEwNTU1MjY5ZjA0Y2ZiYjA0YjY3MWNjZjRiNTVlOCIsImlhdCI6MTY2MDQ3MTE1MSwianRpIjoiY2EyZGFjMzEtMGU1MC00M2VlLWE4MzUtOWFlMzg5Y2M2MTlkIiwidXNlcm5hbWUiOiIxODY2MTg4MzE5MSJ9.soD6KXf_7abZ-of-H72QLvHPCNmw1A0yk1ph6ydFdUE',
			'Host': 'api.qingdao-port.net',
			'Origin': 'https://www.qingdao-port.net',
			'Referer': 'https://www.qingdao-port.net/'
		}
		
		返回 = {}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			
			if 返回值.text in ('', '{}'):
				返回['结果'] = False
				返回['状态码'] = '404'
				返回['状态描述'] = '船舶资料返回空白值'
				返回['原文'] = 'text'
				return 返回
			
			结果 = 返回值.json()
			返回['数量'] = len(结果.get('res', [{}])[0].get('data', []))
			返回['数据'] = []
			返回['原文'] = 返回值.text
			
			if 结果.get('success'):
				数据 = 结果['res'][0].get('data', [])
				返回['数据'] = [{
					'VESSEL_LENTH': i.get('VESSEL_LENTH', ''),
					'VESSEL_GROSS_TONNAGE': i.get('VESSEL_GROSS_TONNAGE', ''),
					'VESSEL_WIDTH': i.get('VESSEL_WIDTH', ''),
					'船公司码': i.get('CGSM', ''),
					'IMO号': i.get('CIMO', ''),
					'VESSEL_CALL_SIGN': i.get('VESSEL_CALL_SIGN', ''),
					'VESSEL_LOAD_TONNAGE': i.get('VESSEL_LOAD_TONNAGE', ''),
					'VESSEL_NET_TONNAGE': i.get('VESSEL_NET_TONNAGE', ''),
					'中文船名': i.get('VESSEL_NAME_CN', ''),
					'船名代码': i.get('CMDM', ''),
					'英文船名': i.get('VESSEL_NAME_EN', ''),
					'VESSEL_TYPE': i.get('VESSEL_TYPE', '')
				} for i in 数据]
				返回['结果'] = True
			else:
				返回['结果'] = False
				返回['状态码'] = '异常'
				返回['状态描述'] = 结果.get('msg', '登录超时,请重新登录')
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(http_err)
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回['状态码'] = '异常'
			返回['状态描述'] = '船舶资料解析失败'
			返回['原文'] = 返回值.text
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(e)
		
		返回['原文'] = 返回值.text
		return 返回
	
	def 港口代码(self, 内容, pageNum='1', pageSize='10', flag='0'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/gkdm.do'
		data = {
			'pageNum': pageNum,
			'pageSize': pageSize,
			'GKZM': 内容,
			'flag': flag
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		返回 = {
			'结果': False,
			'状态码': '异常',
			'状态描述': '请求未完成',
			'原文': ''
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回数据 = 返回值.json()  # 尝试转化为JSON
			
			if 返回数据.get('success', False):
				返回.update({
					'结果': True,
					'状态码': '200',
					'状态描述': '请求成功',
					'数据': 返回数据.get('data', [])
				})
			else:
				返回['状态描述'] = 返回数据.get('msg', '未知错误')
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(http_err)
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回['状态码'] = '异常'
			返回['状态描述'] = '返回数据解析失败'
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态码'] = '异常'
			返回['状态描述'] = str(e)
		
		返回['原文'] = 返回值.text  # 包含原始响应文本
		return 返回
	
	def 单箱查询(self, 箱号, pageNum='1', pageSize='10', JCK="CK", type类型="common"):
		url = 'https://api.qingdao-port.net/oceantally/logistics/wmdx/queryWmdx.do'
		data = {
			"pageNum": pageNum,
			"pageSize": pageSize,
			"searchMap[XH]": 箱号,
			"searchMap[JCK]": JCK,
			"searchMap[type]": type类型
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		
		返回 = {
			'结果': False,
			'状态码': '404',
			'状态描述': '请求未完成',
			'原文': ''
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回json = 返回值.json()  # 尝试转化为JSON
			
			if 返回json.get('success', False):
				返回.update({
					'结果': True,
					'状态码': '200',
					'状态描述': '请求成功',
					'数据': 返回json.get('data', [])
				})
			else:
				返回['状态描述'] = 返回json.get('msg', '未知错误')
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回.update({
				'状态码': '异常',
				'状态描述': str(http_err)
			})
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回['状态描述'] = '返回数据解析失败'
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回.update({
				'状态码': '异常',
				'状态描述': str(e)
			})
		
		返回['原文'] = 返回值.text  # 包含原始响应文本
		return 返回
	
	def 集装箱出口信息查询(self, 箱号, queryType='XH'):
		url = 'https://api.qingdao-port.net/oceantally/track/search/loadData.do'
		data = {
			"queryType": queryType,
			"value": 箱号
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		返回 = {
			'结果': False,
			'状态码': '404',
			'状态描述': '请求未完成',
			'原文': ''
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			
			返回 = 返回值.json()  # 尝试解析JSON
			if not 返回.get('success', False):
				返回['状态描述'] = 返回.get('msg', '未知错误')
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回.update({'状态码': '异常', '状态描述': str(http_err), '原文': '连不上'})
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回.update({'状态码': '异常', '状态描述': '返回数据解析失败', '原文': 返回值.text})
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回.update({'状态码': '异常', '状态描述': str(e), '原文': '连接不上'})
		
		return 返回
	
	def 装箱单错误原因(self, 箱号, MTMC='QQCTU', flag='1', pageNum='1', pageSize='10'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/mtsearch/zxdcwyy.do'
		data = {
			"pageNum": pageNum,
			"pageSize": pageSize,
			"cmhc": 箱号,
			"flag": flag,
			"MTMC": MTMC
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		返回 = {
			'结果': False,
			'状态码': '异常',
			'状态描述': '请求未完成',
			'原文': ''
		}
		
		try:
			返回值 = self.session.get(url=url, params=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回 = 返回值.json()  # 尝试解析JSON
			
			if not 返回.get('success', False):
				返回['状态描述'] = 返回.get('msg', '未知错误')
				return 返回
			
			返回['状态码'] = '200'  # 成功状态码
			返回['状态描述'] = '请求成功'
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态描述'] = str(http_err)
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回['状态描述'] = '返回数据解析失败'
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态描述'] = str(e)
		
		返回['原文'] = 返回值.text if '返回值' in locals() else '连不上'
		return 返回
	
	def 场站整船查询(self, pageNum='1', pageSize='10000', cmhc='XER024W', fxbz='', wpbz='', ldxbz='', MTMC='', lb='',
	                 xzdm='', ZWCM='', auth='CZ'):
		url = 'https://api.qingdao-port.net/oceantally/logistics/vipquerycon/queryShipZCCX.do'
		data = {
			'pageNum': pageNum,
			'pageSize': pageSize,
			'searchMap[cmhc]': cmhc,
			'searchMap[fxbz]': fxbz,
			'searchMap[wpbz]': wpbz,
			'searchMap[ldxbz]': ldxbz,
			'searchMap[MTMC]': MTMC,
			'searchMap[lb]': lb,
			'searchMap[xzdm]': xzdm,
			'searchMap[ZWCM]': ZWCM,
			'searchMap[auth]': auth
		}
		headers = {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
		}
		返回 = {
			'结果': False,
			'状态码': '异常',
			'状态描述': '请求未完成',
			'原文': ''
		}
		
		try:
			返回值 = self.session.post(url, data=data, headers=headers)
			返回值.raise_for_status()  # 检查请求是否成功
			返回 = 返回值.json()  # 尝试解析JSON
			
			if not 返回.get('success', False):
				返回['状态描述'] = 返回.get('msg', '未知错误')
			
			返回['状态码'] = '200'  # 成功状态码
			返回['结果'] = True
		
		except requests.HTTPError as http_err:
			logger.exception(f"HTTP请求失败: {http_err}")
			返回['状态描述'] = str(http_err)
		except ValueError as ve:
			logger.exception(f"JSON解析失败: {ve}")
			返回['状态描述'] = '返回数据解析失败'
		except Exception as e:
			logger.exception(f"未知错误: {e}")
			返回['状态描述'] = str(e)
		
		返回['原文'] = 返回值.text if '返回值' in locals() else '连不上'
		return 返回


class 托盘(QSystemTrayIcon):
	
	def __init__(self, 父窗口):
		try:
			super(托盘, self).__init__(父窗口)
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
		
		self.主窗口 = 父窗口
		self.app = 父窗口.全局控制
		self.设计托盘界面()
		self.组件绑定()
	
	def 设计托盘界面(self):
		try:
			# 禁止默认的closed方法，只能使用qapp.quit()的方法退出程序
			self.app.setQuitOnLastWindowClosed(False)
			
			# 设置托盘图标
			self.setIcon(QIcon(常量.托盘_图标))
			# 设置托盘提示文本
			self.setToolTip("这是雨晨科技程序")
			
			# "设计托盘的菜单，这里我实现了一个二级菜单"
			菜单 = QMenu()
			
			显示消息 = QAction("显示消息", self, triggered=self.xianshixiaoxi)
			显示界面 = QAction('显示', self, triggered=self.主窗口.show)
			退出程序 = QAction('退出', self, triggered=self.tuichu)
			菜单.addActions([显示消息, 显示界面, 退出程序])  # 使用addActions简化代码
			
			self.setContextMenu(菜单)
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 组件绑定(self):
		try:
			# 把鼠标点击弹出消息的信号和槽连接
			# self.messageClicked.connect(self.xiaoxidianji)
			
			# 把鼠标点击图标的信号和槽连接
			self.activated.connect(self.tubiaodianji)
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def tubiaodianji(self, 信号值):
		try:
			if 信号值 in {2, 3}:
				if self.主窗口.isMinimized() or not self.主窗口.isVisible():
					# 若是最小化，则先正常显示窗口，再变为活动窗口（暂时显示在最前面）
					self.主窗口.showNormal()
					self.主窗口.activateWindow()
					self.主窗口.setWindowFlags(Qt.Window)
					self.setVisible(False)
					self.主窗口.show()
				else:
					# 若不是最小化，则最小化
					self.主窗口.showMinimized()
					self.主窗口.setWindowFlags(Qt.SplashScreen)
					# self.主窗口.show()
					self.主窗口.hide()
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def xiaoxidianji(self):
		try:
			pass
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def xianshixiaoxi(self):
		try:
			pass
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def tuichu(self):
		try:
			self.setVisible(False)
			self.主窗口.数据库.close()
			self.主窗口.close()
			qApp.quit()
			sys.exit(0)
		except Exception as e:
			logger.exception(f"退出程序时发生错误: {e}")  # 增加错误处理机制
	
	def 运行(self):
		try:
			self.show()
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")


class 数据库_mysql:
	
	def __init__(self, 主机地址="mysql.sqlpub.com", 用户名="chishuan", 密码="eb83b5355493a2ef", 端口号=3306,
	             数据库名="chishuanlong", 超时时间=10, 数据库驱动='QMYSQL'):
		self.主机地址 = 主机地址
		self.用户名 = 用户名
		self.密码 = 密码
		self.端口号 = 端口号
		self.数据库名 = 数据库名
		self.超时时间 = 超时时间
		self.数据库驱动 = 数据库驱动
		
		self.db = QSqlDatabase.addDatabase(self.数据库驱动)
		self.db.setDatabaseName(self.数据库名)
		self.db.setHostName(self.主机地址)
		self.db.setPort(self.端口号)
		self.db.setUserName(self.用户名)
		self.db.setPassword(self.密码)
		try:
			if not self.db.open():
				logger.error(f"数据库连接失败: {self.db.lastError().text()}")
				return
			
			logger.info('数据库连接成功')
			self.游标 = QSqlQuery(self.db)
			self.db.transaction()
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def __del__(self):
		try:
			if self.db.isOpen():
				self.db.commit()
				logger.info('关闭数据库')
			self.db.close()
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 获取属性值(self, 属性, 默认值):
		query_str = f"select 值 from zhuti where 属性='{属性}'"
		try:
			if not self.游标.exec(query_str):
				logger.error(f"查询属性值失败: {self.游标.lastError().text()}")
				return False
			
			if self.游标.first():
				属性值 = self.游标.value(0)
				return 属性值 if 属性值 is not None else self.插入默认值(属性, 默认值)
			
			return self.插入默认值(属性, 默认值)
		
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return False
	
	def 插入默认值(self, 属性, 默认值):
		SQL语句 = f'insert into zhuti (属性, 值) values("{属性}", {默认值})'
		if self.游标.exec(SQL语句):
			self.db.commit()
			logger.info(f'《{属性}》属性值不存在，插入默认值{默认值}')
			return 默认值
		else:
			self.db.rollback()
			logger.error(f"插入默认值失败: {self.游标.lastError().text()}")
			return False
	
	def 插入多条数据(self, 表名, 数据列表):
		开始时间 = time.time()
		数据库连接 = None
		try:
			数据库连接 = pymysql.connect(host=self.主机地址, user=self.用户名, password=self.密码, port=self.端口号,
			                             database=self.数据库名,
			                             connect_timeout=self.超时时间)
			游标 = 数据库连接.cursor()
			
			if not 游标.execute(f'SHOW FULL COLUMNS FROM {表名}'):
				logger.error(f'----函数：<{__name__}> 更新数据库《{表名}》时,取字段列表失败!{游标.lastError()}')
				return False
			
			字段元组 = 游标.fetchall()
			字段列表 = [i[0] for i in 字段元组 if i[0] != 'id']
			
			if len(字段列表) != len(数据列表[0]):
				logger.warning(f'----函数：<{__name__}> 更新数据库《{表名}》时,字段与数据长度不一致!')
				return False
			
			游标.execute(f"DELETE FROM {表名}")
			
			字段 = ','.join(字段列表)
			参数 = ','.join(['%s'] * len(字段列表))
			SQL语句 = f'INSERT INTO {表名} ({字段}) VALUES ({参数})'
			插入条数 = 游标.executemany(SQL语句, 数据列表)  # 直接赋值插入的条数
			
			数据库连接.commit()
			logger.info(f'《{表名}》成功插入{插入条数}条记录！花费{time.time() - 开始时间}秒。')
			return 插入条数
		
		except Exception as e:
			if 数据库连接:
				数据库连接.rollback()
			logger.exception(f'----函数：<{__name__}> 更新数据库《{表名}》插入多条数据执行失败。错误: {e}')
			return False
		
		finally:
			if 数据库连接:
				数据库连接.close()
	
	def 获取所有表名(self):
		try:
			if not self.游标.exec('show tables'):
				logger.info(self.游标.lastError().text())
				return []
			所有表名 = []
			for i in range(self.游标.size()):
				self.游标.next()
				所有表名.append(self.游标.value(0))
			return 所有表名
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return []
	
	def 判断表是否存在(self, 表名):
		try:
			表名列表 = self.获取所有表名()
			return 表名 in 表名列表
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return False
	
	def 获取所有字段名(self, 表名):
		try:
			if not self.游标.exec(f'SHOW FULL COLUMNS FROM {表名}'):
				logger.info(self.游标.lastError().text())
				return []
			所有字段名 = []
			for i in range(self.游标.size()):
				self.游标.next()
				所有字段名.append(self.游标.value(0))
			return 所有字段名
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return []
	
	def 执行SQL语句_针对读操作返回结果集(self, SQL语句='', 信息=''):
		try:
			if not self.游标.exec(SQL语句):
				logger.info(f'{信息}执行失败。{self.游标.lastError()}')
				return False
			
			logger.info(f'{信息}执行成功!得到{self.游标.size()}条记录。')
			return self.游标
		except Exception as e:
			logger.exception(
				f'【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错: {信息}发现异常,SQL语句:{SQL语句},{e}')
			return False
	
	def 执行SQL语句_针对写操作(self, SQL语句='', 信息=''):
		try:
			if not self.游标.exec(SQL语句):
				self.db.rollback()
				logger.info(f'{信息}执行失败。{self.游标.lastError()}')
				return False
			
			条数 = self.游标.numRowsAffected()
			self.db.commit()
			logger.info(f'{信息}执行成功,受影响的记录数:{条数}。')
			return 条数
		except Exception as e:
			logger.exception(
				f'【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错: {信息}发现异常,SQL语句:{SQL语句},{e}')
			self.db.rollback()
			return False
	
	def 创建数据库表(self, 表名, 属性键值对=None, 主外键约束=''):
		try:
			属性键值对 = 属性键值对 or {}
			if self.判断表是否存在(表名):
				logger.info(f'《{表名}》表已经存在')
				return True
			
			语句_唯一字段 = 'id INT AUTO_INCREMENT PRIMARY KEY'
			语句 = ', '.join(f'{键} {值}' for 键, 值 in 属性键值对.items())
			SQL语句 = f'CREATE TABLE {表名} ({语句_唯一字段}{", " + 语句 if 语句 else ""}){主外键约束}'
			
			return self.执行SQL语句_针对写操作(SQL语句, 信息=f'创建《{表名}》')
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return False
	
	def 删除数据库表(self, 表名):
		try:
			if not self.判断表是否存在(表名):
				logger.info(f'《{表名}》表不存在')
				return True
			
			SQL语句 = f"DROP TABLE {表名}"
			return self.执行SQL语句_针对写操作(SQL语句, 信息=f'删除《{表名}》')
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return False
	
	def 插入数据(self, 表名, 属性键列表=None, 属性值列表=None):
		if not 属性键列表 or not 属性值列表:
			logger.error("属性键列表或属性值列表不能为空")
			return False
		
		字段 = ','.join(属性键列表)
		参数 = '","'.join(属性值列表)
		SQL语句 = f'INSERT INTO {表名} ({字段}) VALUES ("{参数}")'
		
		return self.执行SQL语句_针对写操作(SQL语句, 信息=f'《{表名}》插入1条记录。')
	
	def 更新数据(self, 表名, 更新字典=None, 更新条件=None):
		更新字典 = 更新字典 or {}
		更新条件 = 更新条件 or {}
		
		if not 更新字典:
			logger.warning("更新字典为空，未执行更新操作。")
			return False
		
		更新语句 = ', '.join(f'{键}="{值}"' for 键, 值 in 更新字典.items())
		条件语句 = ' and '.join(f'{键}="{值}"' for 键, 值 in 更新条件.items())
		
		SQL语句 = f"UPDATE {表名} SET {更新语句} WHERE {条件语句}" if 条件语句 else ""
		if 条件语句:
			return self.执行SQL语句_针对写操作(SQL语句, 信息=f'《{表名}》更新记录！')
		else:
			logger.warning("更新条件为空，未执行更新操作。")
			return False
	
	def 删除数据(self, 表名, 删除条件字典=None):
		if 删除条件字典:
			条件语句 = ' and '.join([f'{键}="{值}"' for 键, 值 in 删除条件字典.items()])
			SQL语句 = f"DELETE FROM {表名} WHERE {条件语句}"
			return self.执行SQL语句_针对写操作(SQL语句, 信息=f'《{表名}》删除记录！')
		else:
			logger.warning("删除条件字典为空，未执行删除操作。")
			return False
	
	def 查询数据(self, 表名, 字段列表=None, 查询条件=None):
		
		字段列表 = 字段列表 or []
		条件语句 = '1=1' if 查询条件 is None else ' and '.join([f'{k}="{v}"' for k, v in 查询条件.items()])
		字段 = '*' if not 字段列表 else ','.join(字段列表)
		SQL语句 = f"SELECT {字段} FROM {表名} WHERE {条件语句}"
		
		结果 = self.执行SQL语句_针对读操作返回结果集(SQL语句, 信息=f'查询{字段列表}数据')
		结果列表 = []
		
		if 结果:
			while 结果.next():
				行数据 = [结果.value(i) for i in range(len(字段列表) or 1)]
				结果列表.append(行数据)
		
		return 结果列表
	
	def 查询数据_自定义条件(self, 表名, 字段列表=None, 查询条件=''):
		字段 = '*' if 字段列表 is None or len(字段列表) == 0 else ','.join(字段列表)
		SQL语句 = f"select {字段} from {表名} where {查询条件}" if 查询条件 else f"select {字段} from {表名} where 1=1"
		结果 = self.执行SQL语句_针对读操作返回结果集(SQL语句, 信息='查询数据')
		
		if 结果:
			return [[结果.value(i) for i in range(len(字段列表) or 1)] for _ in iter(lambda: 结果.next(), False)]
		
		return []


class Excel文件:
	def __init__(self, 文件, 模式='打开'):
		try:
			self.文件 = 文件
			self.app = xlwings.App(visible=False, add_book=False)  # 程序可见，只打开不新建工作薄
			self.app.display_alerts = False  # 警告关闭
			self.app.screen_updating = False  # 屏幕更新关闭，可以减少卡顿
			
			if 模式 == '打开':
				self.Excel文件 = self.app.books.open(文件)
				self.Excel表格 = self.Excel文件.sheets.active
			elif 模式 == '新建':
				self.Excel文件 = self.app.books.add()
				self.Excel文件.app()
				self.Excel文件.save(文件)
				self.Excel表格 = self.Excel文件.sheets.active
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 保存(self):
		try:
			self.Excel文件.save()  # 保存文件
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def __del__(self):
		try:
			self.Excel文件.close()  # 关闭文件
			self.app.quit()  # 关闭程序
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 获取指定表格(self, 指定表格):
		try:
			self.Excel表格 = self.Excel文件.sheets(指定表格)  # 指定名称获取sheet工作表
			self.Excel表格 = self.Excel文件.sheets(1)  # 根据序号获取
			return self.Excel表格
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 读取内容(self, 范围):
		try:
			return self.Excel表格[范围].value
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 写入数据(self, 内容, 范围, 横纵='横'):
		try:
			if 横纵 == '横':
				self.Excel表格.range(范围).value = 内容
			elif 横纵 == '纵':
				self.Excel表格.range(范围).options(transpose=True).value = 内容
			else:
				self.Excel表格.range(范围).options(expand='table').value = 内容
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 删除(self, 位置, 行为='left'):
		try:
			self.Excel表格[位置].delete(行为)  # 删除单元格 (缺省默认右侧左移'left'，下侧上移'up')
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 插入(self, 位置, 行为='down'):
		try:
			self.Excel表格[位置].insert(行为)  # 插入单元格 (缺省默认'down', 可选 'right')
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 获取数据范围(self):
		try:
			# # 方法二
			# nrow = self.Excel表格.api.UsedRange.Rows.count
			# ncol = self.Excel表格.api.UsedRange.Columns.count
			# print(nrow)
			# print(ncol)
			
			# 方法一
			shape = self.Excel表格.used_range.shape
			return shape
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 读列宽(self, 列号):
		try:
			return self.Excel表格.range(列号).column_width
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 读行高(self, 行号):
		try:
			return self.Excel表格.range(行号).row_height
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 置列宽(self, 列号):
		self.Excel表格.range(列号).column_width = 10
	
	def 置行高(self, 行号):
		try:
			self.Excel表格.range(行号).row_height = 15
		
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def 获取公式(self, 位置):
		return self.Excel表格.range(位置).formula_array
	
	def 设置公式(self, 位置, 公式):
		self.Excel表格.range(位置).formula = 公式
	
	def 获取颜色(self, 位置):
		
		return self.Excel表格.range(位置).color
	
	def 设置颜色(self, 位置, 颜色):
		self.Excel表格.range(位置).color = 颜色
	
	def 清除颜色(self, 位置):
		
		self.Excel表格.range(位置).color = None
	
	def 自适应列宽(self, 范围=''):
		if 范围 == '':
			self.Excel表格.autofit()
		else:
			self.Excel表格[范围].columns.autofit()
	
	def 自适应行高(self, 范围=''):
		if 范围 == '':
			self.Excel表格.autofit()
		else:
			self.Excel表格[范围].rows.autofit()
	
	def 自适应行高列宽(self):
		# 	# 自动调整行高列宽
		# 	sht.autofit('c')
		self.Excel表格.autofit()
	
	def 合并单元格(self, 范围):
		# 	rng.merge_area  # 返回合并单元格区域
		# 	rng.merge_cells  # 返回True或者False，测试是否在合并单元格区域
		self.Excel表格[范围].rng.merge(across=False)
	
	def 拆分单元格(self, 范围):
		self.Excel表格[范围].unmerge()
	
	def 清除工作表所有内容和格式(self):
		self.Excel表格.clear()
	
	def 清除工作表的所有内容但是保留原有格式(self):
		self.Excel表格.clear_contents()
	
	def 删除工作表(self):
		self.Excel表格.delete()
	
	def 其他(self):
		
		# 	# 在活动工作簿中选择
		# 	sht.select()
		
		# 	# 可以通过属性获取获取工作表的名称、所有单元格的区域对象、当前工作表的索引值
		# 	# sht.name
		# 	# sht.cells
		# 	# sht.index
		# 	# sht.names
		#
		# 	# range.offset(row_offset=5, column_offset=2)
		# 	# 表示偏移，row_offset行偏移量（正数表示向下偏移，负数相反），column_offset列偏移量（正数表示向右偏移，负数相反）
		# 	# 注意：是将选区范围进行偏移，内容不进行偏移
		
		# 	# range.expand(mode=’down’)
		# 	扩展区域，参数可选取 ‘down’, ‘right’, ’table’ ，类似我们使用向下、向右或者下右方的区域扩展操作。
		
		# 	# range.resize(row_size=4, column_size=2)
		# 	# 表示调整选中区域的大小，参数表示调整后区域的行、列的数量。
		
		# 	# range.current_region
		# 	# 表示全选,类似Ctrl + A
		
		# # 获取数字格式
		# 	rng.number_format
		
		# 	# 设置数字格式
		# 	rng.number_format = '0.00%'
		
		# 	rng.insert(shift=None, copy_origin='format_from_left_or_above')
		#
		# 	# 返回区域第一行的行号
		# 	rng.row
		#
		# 	# 返回区域的第一列的号，注意返回的列号不是ABCD，而是1234
		# 	rng.column
		
		pass


class MyQSqlTableModel(QSqlTableModel):
	"""
	重写QSqlTableModel的data函数
	"""
	
	def __init__(self):
		try:
			super().__init__()  # 使用super()来初始化父类
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
	
	def data(self, index, role=Qt.DisplayRole):
		if not index.isValid():
			return None
		
		try:
			if role == Qt.TextAlignmentRole:
				return Qt.AlignCenter
			return super().data(index, role)  # 使用super()简化代码
		except Exception as e:
			logger.exception(f"【{__class__.__name__}】中的<{sys._getframe().f_code.co_name}>出错,{e}")
			return None  # 返回None或其他适当值应对异常情况


class 管理基类(QMainWindow):
	
	def __init__(self, 模块, 数据库表名=None, 父窗口=None):
		self.父窗口 = 父窗口
		self.数据库 = 父窗口.数据库 if 父窗口 else 数据库_mysql()
		self.模块 = 模块
		self.条件 = None
		self.数据库表名 = 数据库表名 or 'beiyongming'
		self.模型 = MyQSqlTableModel()
		
		try:
			super().__init__(父窗口)
			self.setupUi(self)
			
			self.模型设置(self.数据库表名)
			self.组件设置()
			self.事件绑定()
		except Exception as e:
			logger.exception(f"初始化出错: {e}")
			self.父窗口.statusbar.showMessage(f"初始化出错: {e}", 2000)
	
	def 组件设置(self):
		try:
			self.statusbar.showMessage(f"{self.模块}", 2000)
			
			self.tableView.setEditTriggers(QTableView.NoEditTriggers)
			self.tableView.setSortingEnabled(True)
			self.tableView.setSelectionBehavior(QTableView.SelectRows)
			
			self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
			self.tableView.horizontalHeader().setStretchLastSection(True)
			self.tableView.horizontalHeader().setStyleSheet(
				"QHeaderView::section{background-color:rgb(40,143,218);font:10pt '宋体';color: white;}"
			)
		except Exception as e:
			logger.exception(f"组件设置出错: {e}")
			self.父窗口.statusbar.showMessage(f"组件设置失败: {e}", 2000)
	
	def 事件绑定(self):
		try:
			按钮与方法连接 = [
				(self.pushButton_tianjia, self.tianjia),
				(self.pushButton_shanchu, self.shanchu),
				(self.pushButton_xiugai, self.xiugai),
				(self.pushButton_chaxun, self.chaxun),
				(self.pushButton_quxiao, self.quxiao),
				(self.pushButton_baocun, self.baocun),
				(self.pushButton_shuaxin, self.shuaxin),
				(self.pushButton_qingkong, self.qingkong),
				(self.pushButton_biaoji, self.biaoji),
				(self.pushButton_daochu, self.daochu)]
			
			for 按钮, 方法 in 按钮与方法连接:
				按钮.clicked.connect(方法)
			
			self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
			self.tableView.customContextMenuRequested[QPoint].connect(self.youjibiaoge)
		except Exception as e:
			logger.exception(f"事件绑定出错: {e}")
			self.父窗口.statusbar.showMessage(f"事件绑定出错: {e}", 2000)
	
	def 模型设置(self, 数据库表名):
		try:
			self.模型.setTable(数据库表名)
			self.模型.setEditStrategy(QSqlTableModel.OnManualSubmit)
			
			self.模型.select()
			self.tableView.setModel(self.模型)
			
			self.行数 = self.模型.rowCount()
			self.列数 = self.模型.columnCount()
			self.标题 = [self.模型.headerData(i, Qt.Horizontal) for i in range(self.列数)]
		except Exception as e:
			logger.exception(f"设置模型出错: {e}")
			self.父窗户.statusbar.showMessage(f"模型设置出错: {e}", 2000)
	
	def tianjia(self):
		try:
			row = self.tableView.currentIndex().row()
			row = row if row != -1 else self.模型.rowCount()
			self.模型.insertRow(row)
			self.模型.setData(self.模型.index(row, 1), "待输入...")
			self.父窗口.statusbar.showMessage('添加')
		except Exception as e:
			logger.exception(f"添加操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"添加操作出错: {e}", 2000)
	
	def shanchu(self):
		try:
			row = self.tableView.currentIndex().row()
			if row != -1:
				self.模型.removeRow(row)
				self.父窗口.statusbar.showMessage(f'删除第{row}行！')
			else:
				self.父窗口.statusbar.showMessage('没有被选择的行')
		except Exception as e:
			logger.exception(f"删除操作失败: {e}")
			self.父窗口.statusbar.showMessage(f"操作失败: {e}", 2000)
	
	def xiugai(self):
		try:
			self.tableView.setEditTriggers(QTableView.DoubleClicked)
			self.父窗口.statusbar.showMessage('允许修改')
		except Exception as e:
			logger.exception(f"修改状态设置出错: {e}")
			self.父窗口.statusbar.showMessage(f"修改状态设置出错: {e}", 2000)
	
	def baocun(self):
		try:
			if self.模型.submitAll():
				self.tableView.setEditTriggers(QTableView.NoEditTriggers)
				self.父窗口.statusbar.showMessage('数据已保存')
			else:
				self.父窗口.statusbar.showMessage('数据保存失败', 2000)
		except Exception as e:
			logger.exception(f"保存数据出错: {e}")
			self.父窗口.statusbar.showMessage(f"保存过程中发生错误: {e}", 2000)
	
	def quxiao(self):
		try:
			self.模型.select()
			self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
			self.父窗口.statusbar.showMessage('取消')
		except Exception as e:
			logger.exception(f"取消操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"取消操作出错: {e}", 2000)
	
	def chaxun(self):
		if not self.条件:
			self.父窗口.statusbar.showMessage("查询条件为空，无法执行查询", 2000)
			return
		
		try:
			self.模型.setFilter(self.条件)
			if not self.模型.select():
				logger.warning("查询无结果")
				self.父窗口.statusbar.showMessage("查询无结果", 2000)
		except Exception as e:
			logger.exception(f"查询出错: {e}")
			self.父窗口.statusbar.showMessage(f"查询过程中发生错误: {e}", 2000)
	
	def shuaxin(self):
		try:
			if self.模型.canFetchMore():
				self.模型.select()
			self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
			self.父窗口.statusbar.showMessage('刷新成功')
		except Exception as e:
			logger.exception(f"刷新操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"刷新操作失败: {e}", 2000)
	
	def biaoji(self):
		try:
			self.tableView.setEditTriggers(QTableView.DoubleClicked | QTableView.EditKeyPressed)
			row = self.tableView.currentIndex().row()
			
			if row == -1:
				self.父窗口.statusbar.showMessage("没有选中行，无法标记")
				return
			
			mark_col = next((i for i, title in enumerate(self.标题) if title == '标记状态'), 0)
			current_value = self.模型.data(self.模型.index(row, mark_col))
			
			if current_value != '标记':
				if self.数据库表名 == "yonghu":
					for i in range(self.行数):
						self.模型.setData(self.模型.index(i, mark_col), "")
				self.模型.setData(self.模型.index(row, mark_col), '标记')
			else:
				self.模型.setData(self.模型.index(row, mark_col), "")
			
			if not self.模型.submitAll():
				self.父窗口.statusbar.showMessage("标记操作失败，提交更改时出现错误")
			
			self.tableView.setEditTriggers(QTableView.NoEditTriggers)
			self.父窗口.statusbar.showMessage('标记')
		except Exception as e:
			logger.exception(f"标记操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"操作过程中出现错误: {e}", 2000)
	
	def qingkong(self):
		退出提示框 = QMessageBox.question(self.父窗口, "提示", f"你确定要清空《{self.数据库表名}》？",
		                                  QMessageBox.Yes | QMessageBox.No,
		                                  QMessageBox.No)
		if 退出提示框 == QMessageBox.Yes:
			try:
				query = QSqlQuery()
				if not query.exec(f'DELETE FROM {self.数据库表名}'):
					raise Exception(f"删除操作失败: {query.lastError().text()}")
				
				self.模型.select()
				self.父窗口.statusbar.showMessage('清空成功')
			except Exception as e:
				logger.exception(f"清空操作出错: {e}")
				self.父窗口.statusbar.showMessage(f"清空操作出错: {e}", 2000)
	
	def youjibiaoge(self, pos):
		try:
			右击菜单 = QMenu()
			添加一行 = 右击菜单.addAction('添加一行')
			删除选中行 = 右击菜单.addAction('删除选中行')
			标记 = 右击菜单.addAction('标记')
			备用1 = 右击菜单.addAction('备用1')
			备用2 = 右击菜单.addAction('备用2')
			
			当前选择 = 右击菜单.exec_(self.tableView.mapToGlobal(pos))
			
			操作字典 = {
				删除选中行: self.shanchu,
				标记: self.biaoji,
				添加一行: self.tianjia,
				备用1: self.beiyong1,
				备用2: self.beiyong2
			}
			
			操作 = 操作字典.get(当前选择)
			if 操作:
				操作()
			else:
				logger.warning("未识别的菜单选择")
		except Exception as e:
			logger.exception(f"右键菜单操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"操作过程中出现错误: {e}", 2000)
	
	def daochu(self):
		try:
			workbook = xlwt.Workbook()
			worksheet = workbook.add_sheet("新建工作表")
			
			self.行数 = self.模型.rowCount()
			self.列数 = self.模型.columnCount()
			
			for 列 in range(self.列数):
				worksheet.write(0, 列, self.标题[列])
			
			for 行 in range(1, self.行数 + 1):
				for 列 in range(self.列数):
					worksheet.write(行, 列, self.模型.item(行 - 1, 列).text())
			
			key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
			                     r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
			文件名 = winreg.QueryValueEx(key, "Desktop")[0] + f'/{self.数据库表名}{int(time.time())}.xls'
			
			workbook.save(文件名)
			self.父窗口.statusbar.showMessage(文件名 + '导出成功！')
		except Exception as e:
			logger.exception(f"导出操作出错: {e}")
			self.父窗口.statusbar.showMessage(f"导出操作出错: {e}", 2000)
	
	def qiehuan(self):
		try:
			self.模型设置(self.comboBox.currentText())
		except Exception as e:
			logger.exception(f"切换模型出错: {e}")
			self.父窗口.statusbar.showMessage(f"切换模型出错: {e}", 2000)
	
	def do_currentChanged(self, a):
		try:
			logger.debug(a.row())
		except Exception as e:
			logger.exception(f"当前变化处理出错: {e}")
			self.父窗口.statusbar.showMessage(f"当前变化处理出错: {e}", 2000)
	
	def do_currentRowChanged(self, a):
		try:
			logger.debug(a.row())
		except Exception as e:
			logger.exception(f"当前行变化处理出错: {e}")
			self.父窗口.statusbar.showMessage(f"当前行变化处理出错: {e}", 2000)


工具类行数 = sys._getframe().f_lineno